/**
 * 
 */
package truerefactor.refactor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import truerefactor.graph.Accessibility;
import truerefactor.graph.ClassNode;
import truerefactor.graph.CodeGraph;
import truerefactor.graph.CodeNode;
import truerefactor.graph.Edge;
import truerefactor.graph.EdgeType;
import truerefactor.graph.MethodNode;
import truerefactor.graph.SetOperations;
import truerefactor.graph.StatementNode;
import truerefactor.graph.StatementType;

/**
 * @author Isaac
 */
public class DecomposeConditional extends Refactoring {

    /**
     * 
     */
    public DecomposeConditional()
    {
        this.shortName = "DECOND";
        this.name = "Decompose Conditional";
        this.description = "";
    }

    /*
     * (non-Javadoc)
     * @see
     * truerefactor.refactor.Refactoring#execute(truerefactor.graph.CodeGraph )
     */
    @Override
    public void execute(CodeGraph graph)
    {
        StatementNode node = (StatementNode) operands.get(0);
        MethodNode parent = node.getParent();
        ClassNode source = parent.getParentClass();

        // 1. Extract the condition into its own method
        String queryName = getQueryName(node);
        MethodNode mNode = new MethodNode("isDeCond" + queryName, "");
        mNode.setAccessiblity(Accessibility.Protected);

        // TODO figure out how to set the correct parameters

        CodeGraph stmtGraph = parent.getStatementGraph();
        String rightHalf = node.getCode();

        StatementNode sNode = new StatementNode(mNode, StatementType.RETURN, "return " + rightHalf);
        node.setCode(queryName + " = " + mNode.getIdentifier() + "();");

        source.addMethod(mNode);
        mNode.getStatementGraph().insert(sNode);

        mNode.addAssociatedMethod(parent);
        parent.addAssociatedMethod(mNode);

        graph.insert(mNode);
        graph.insert(new Edge(source, mNode, EdgeType.Method));

        // 2. Extract the then part and the else part into their own methods
        List<CodeNode> nodes = stmtGraph.getAdjacencies(node);
        Set<CodeNode> endNode = new HashSet<CodeNode>();
        for (CodeNode stmt : nodes)
        {
            if (stmt instanceof StatementNode)
            {
                Set<CodeNode> temp = new HashSet<CodeNode>();
                temp.addAll(stmtGraph.getAdjacencies(stmt));

                if (!endNode.isEmpty())
                {
                    Set<CodeNode> temps = SetOperations.union(endNode, temp);
                    endNode = temps;
                }
            }
        }

        int count = 1;
        for (CodeNode stmt : nodes)
        {
            MethodNode next = new MethodNode("get" + queryName + count, "");
            next.setAccessiblity(Accessibility.Protected);
            graph.cloneStatementGraph(next, stmtGraph);
            

            // figure out how to set the correct parameters

            if (stmt instanceof StatementNode)
            {
                Set<CodeNode> diff = new HashSet<CodeNode>();
                Set<CodeNode> stmtList = new HashSet<CodeNode>();
                do
                {
                    diff = endNode;
                    if (stmtList.isEmpty())
                    {
                        stmtList.addAll(stmtGraph.getAdjacencies(stmt));
                        continue;
                    }

                    Set<CodeNode> toAdd = new HashSet<CodeNode>();
                    for (CodeNode cNode : stmtList)
                    {
                        if (cNode instanceof StatementNode)
                        {
                            toAdd.addAll(stmtGraph.getAdjacencies(cNode));
                        }
                    }

                    stmtList.addAll(toAdd);
                    Set<CodeNode> temp = SetOperations.difference(diff, stmtList);
                    diff = temp;
                }
                while (!diff.isEmpty());

                for (CodeNode nextStmt : stmtList) {
                    next.getStatementGraph().remove(nextStmt);
                    next.getStatements().remove(nextStmt);
                }
                
                for (CodeNode nextStmt : stmtList) {
                    stmtGraph.remove(nextStmt);
                    parent.getStatements().remove(nextStmt);
                }
                
                graph.insert(next);
                graph.insert(new Edge(source, next, EdgeType.Method));
            }
        }
    }

    /**
     * 
     * @param node
     * @return
     */
    private String getQueryName(StatementNode node)
    {
        int count = 0;
        String retVal = "";
        ClassNode parent = node.getParent().getParentClass();
        
        for (CodeNode method : parent.getMethods()) {
            if (method.getIdentifier().contains("isDeCond")) {
                count++;
            }
        }
        
        if (count > 0) {
            retVal += count;
        }
        
        return retVal;
    }
}
